/**
 *  Copyright 2013 Nate Dierk
 *
 *  COMP 590 -- Fall 2013
 */

#define MAX_MATRICES 64

#include "GMatrix_nd.h"
#include "GBlitter_nd.cpp"

class GContext0 : public GContext {
protected:
  GBitmap fBmp;
  GMatrix fMatStack[MAX_MATRICES];
  int fStackTop;

  /////////////////////////////////////////////////////////
  // Helper Functions

  GMatrix* getMatrix(){
  	return &fMatStack[fStackTop];
  };

  void blitAllPixels(GBlitter* blitter){
  	int count = fBmp.fWidth, row_max = fBmp.fHeight;
  	for (int row = 0; row < row_max; ++row){
  		blitter->blitRow(0, row, count);
  	}
  }

  void blitRect(const GIRect& rect, GBlitter* blitter){
   for (int row = rect.fTop; row < rect.fBottom; ++row){
  		blitter->blitRow(rect.x(), row, rect.width());
  	}
  }

public:
    GContext0(const GBitmap& bmp) {
      fBmp = bmp;
      fStackTop = 0;
      fMatStack[0] = GMatrix(); //set matrix to identity
  }
    virtual ~GContext0() {}


    // *  Return the information about the context's bitmap
    void getBitmap(GBitmap* bmp) const {
      *bmp = fBmp;
    }
   
    ///////////////////////////////////////////////////////
    // Drawing Functions

    void clear(const GColor& color){
      ClearBlitter cb = ClearBlitter(&fBmp, color);
     
      // if no slop
      if (fBmp.fRowBytes == fBmp.fWidth * sizeof(GPixel)){ 
        // blit all pixels at once
        cb.blitRow(0, 0, fBmp.fWidth * fBmp.fHeight); 
      } else { //otherwise blit each row
        for (int row = 0; row < fBmp.fHeight; ++row){
          cb.blitRow(0, row, fBmp.fWidth);
        }
      }     
    }

    void drawRect(const GRect& rect, const GPaint& paint){
      if (0.0f == paint.getAlpha()) { return; } // if 0 alpha quit

      GIRect xformed = getMatrix()->xformRect(rect).round(); // transform rectangle
      GIRect clip;

      if (clip.setIntersection(xformed, GIRect::MakeWH(fBmp.fWidth, fBmp.fHeight))) { // clip to intersection with bmp
        RectBlitter blit = RectBlitter(&fBmp, paint);
        blitRect(clip, &blit); //blit the clipped rect
      }
    }

    void drawBitmap(const GBitmap& bmp, float x, float y, const GPaint& paint){
      if (0.0f == paint.getAlpha()) { return; } // if 0 alpha quit

      onSave();
      translate(x, y);

      GIRect xformed = getMatrix()->xformRect(GIRect::MakeWH(bmp.fWidth, bmp.fHeight)).round();
      GIRect clip;
      GMatrix inv = getMatrix()->inverse();

      if (clip.setIntersection(xformed, GIRect::MakeWH(fBmp.fWidth, fBmp.fHeight))) {
        //create blitter and blit all pixels
      	BmpBlitter blit = BmpBlitter(&fBmp, bmp, &inv, paint.getAlpha());
      	blitAllPixels(&blit);
      }

      onRestore();
    }

    ///////////////////////////////////////////////////////
    // CTM modification functions

    void translate(float x, float y){
      getMatrix()->precat(GMatrix::translate(x,y));
    }

    void scale(float x, float y){
      getMatrix()->precat(GMatrix::scale(x,y));
    }

    
    void onSave(){
      if (fStackTop+1 < MAX_MATRICES){
        fMatStack[fStackTop+1] = fMatStack[fStackTop]; //duplicate matrix
        ++fStackTop;
      }
    }

    void onRestore(){
      if (fStackTop > 0){
      --fStackTop; // pop off most recent changes
    }
  }

};